/**
 * 
 */
package com.celephais.common.client.ui;

import java.util.List;

import com.celephais.common.client.TransferObjectAccessor;
import com.celephais.common.client.model.DataFilter;
import com.celephais.common.client.model.TableColumn;
import com.celephais.common.client.model.TransferObject;
import com.celephais.common.client.model.TransferObjectCreator;
import com.celephais.common.client.model.UnimplementedException;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * @author Eugene Shen
 * 
 * This class is meant to be used as a glue to connect two Widgets that
 * represent a parent-child relationship.  As such, it is actually
 * synchronous despite signaling back via async callbacks.
 */
public class ChildCollectionEditorCallback<T extends TransferObject> implements
		TransferObjectAccessor<T>, TransferObjectCreator {

	private TransferObject parentObj;
	private String childColName;
	
	public ChildCollectionEditorCallback() {
		super();
	}
	
	public ChildCollectionEditorCallback(T obj, String childColName) {
		this();
		setParentObj(obj);
		setChildColName(childColName);
	}
	
	public void setParentObj(TransferObject parentObj) {
		this.parentObj = parentObj;
	}
	public TransferObject getParentObj() {
		return parentObj;
	}
	
	public void setChildColName(String childColName) {
		this.childColName = childColName;
	}
	public String getChildColName() {
		return childColName;
	}
	
	@Override
	public void add(T obj, AsyncCallback<Boolean> callback) {
		Throwable sanityCheck = performSanityCheck();
		if (sanityCheck != null) {
			callback.onFailure(sanityCheck);
		}
		else {
			try {
				if (getParentObj().addChild(getChildColName(), -1, obj)) {
					callback.onSuccess(true);
				}
				else {
					// TODO: Is this right?  Or should we call onFailure?
					callback.onSuccess(false);
				}
			}
			catch (UnimplementedException e) {
				callback.onFailure(e);
			}
		}
	}

	@Override
	public void delete(T obj, AsyncCallback<Boolean> callback) {
		Throwable sanityCheck = performSanityCheck();
		if (sanityCheck != null) {
			callback.onFailure(sanityCheck);
		}
		else {
			try {
				List<? extends TransferObject> children = getParentObj().getChildren(getChildColName());
				int idx = children.indexOf(obj);
				if (getParentObj().removeChild(getChildColName(), idx)) {
					callback.onSuccess(true);
				}
				else {
					// TODO: Is this right?  Or should we call onFailure?
					callback.onSuccess(false);
				}
			}
			catch (UnimplementedException e) {
				callback.onFailure(e);
			}
		}
	}

	@Override
	public void getColumns(AsyncCallback<TableColumn[]> callback) {
		Throwable sanityCheck = performSanityCheck();
		if (sanityCheck != null) {
			callback.onFailure(sanityCheck);
		}
		else {
			try {
				TransferObject newChild = getParentObj().createChild(getChildColName());
				callback.onSuccess(newChild.getColumns());
			}
			catch (UnimplementedException e) {
				callback.onFailure(e);
			}
		}
	}

	@Override
	public void getCount(DataFilter[] filters, AsyncCallback<Integer> callback) {
		Throwable sanityCheck = performSanityCheck();
		if (sanityCheck != null) {
			callback.onFailure(sanityCheck);
		}
		else {
			// TODO: Implement Data Filters...?
			try {
				int count = getParentObj().getChildren(getChildColName()).size();
				callback.onSuccess(count);
			}
			catch (UnimplementedException e) {
				callback.onFailure(e);
			}
		}
	}

	@Override
	public void getObjects(int startRow, int rowsCount, DataFilter[] filters,
			String sortColumnName, boolean sortOrder,
			AsyncCallback<List<T>> callback) {
		Throwable sanityCheck = performSanityCheck();
		if (sanityCheck != null) {
			callback.onFailure(sanityCheck);
		}
		else {
			// TODO: Implement filters, row #s, etc...?
			try {
				// It's really hard to make this cast safe...
				List<T> children = (List<T>)getParentObj().getChildren(getChildColName());
				callback.onSuccess(children);
			}
			catch (UnimplementedException e) {
				callback.onFailure(e);
			}
		}
	}

	@Override
	public void update(T obj, AsyncCallback<Boolean> callback) {
		// TODO: Do nothing?  Given the nature of how we provide the list,
		// it should be edited in place.
		callback.onSuccess(true);
	}

	private Throwable performSanityCheck() {
		if (getParentObj() == null) {
			return new Exception("Parent Obj is not set.");
		}
		else if (getChildColName() == null) {
			return new Exception("Child Column Name is not set.");
		}
		else {
			return null;
		}
	}

	@Override
	public TransferObject newTransferObject() {
		Throwable sanityCheck = performSanityCheck();
		if (sanityCheck != null) {
			return null;
		}
		else {
			try {
				return getParentObj().createChild(getChildColName());
			}
			catch (UnimplementedException e) {
				return null;
			}
		}
	}
}
